package vip.aster.common.utils;

import cn.hutool.core.util.StrUtil;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.jcache.JCacheCache;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p> ehcache工具类 </p>
 *
 * @author Aster
 * @since 2023/11/21 17:22
 */
public class CacheUtils {

    /**
     * 用于ehcache队列
     */
    public static Map<String, Queue<Object>> queueMap = new HashMap<>();

    /**
     * 使用redis时对redis进行单独特殊操作需要使用
     *
     * @param <K> 键
     * @param <V> 值
     * @return redisTemplate
     */
    public static <K, V> RedisTemplate<K, V> getRedisTemplate() {
        return SpringUtils.getBean("redisTemplate");
    }

    /**
     * 获取CacheManager
     *
     * @return cacheManager
     */
    public static CacheManager getCacheManager() {
        return SpringUtils.getBean(CacheManager.class);
    }

    /**
     * 根据cacheName从CacheManager中获取cache
     *
     * @param cacheName 缓存名称
     * @return cache
     */
    public static Cache getCache(String cacheName) {
        return getCacheManager().getCache(cacheName);
    }

    /**
     * 获取缓存的所有key值(由于springcache不支持获取所有key,只能根据cache类型来单独获取)
     *
     * @param cacheName 缓存名称
     * @return set
     */
    @SuppressWarnings(value = {"unchecked"})
    public static Set<String> keys(String cacheName) {
        Cache cache = getCacheManager().getCache(cacheName);
        Set<String> keyset = new HashSet<>();
        if (cache == null) {
            return keyset;
        }
        if (cache instanceof JCacheCache jCacheCache) {
            for (javax.cache.Cache.Entry<Object, Object> next : jCacheCache.getNativeCache()) {
                Object key = next.getKey();
                keyset.add(key.toString());
            }
        } else if (cache instanceof TransactionAwareCacheDecorator) {
            Set<Object> keys = getRedisTemplate().keys(cache.getName() + "*");
            assert keys != null;
            for (Object s : keys) {
                keyset.add(StrUtil.replace(s.toString(), cache.getName() + ":", ""));
            }
        }
        return keyset;
    }

    /**
     * 根据cacheName,key缓存数据
     *
     * @param cacheName 缓存名称
     * @param key 键
     * @param value 值
     * @param <T> T
     */
    public static <T> void set(String cacheName, String key, T value) {
        set(cacheName, key, value, 0, null);
    }

    /**
     * 如果没有则进行缓存,根据cacheName,key缓存数据
     *
     * @param cacheName 缓存名称
     * @param key 键
     * @param value 值
     * @param <T> T
     */
    public static <T> void setIfAbsent(String cacheName, String key, T value) {
        if (ObjectUtils.isEmpty(get(cacheName, key))) {
            set(cacheName, key, value, 0, null);
        }
    }

    /**
     * 根据cacheName,key和缓存过期时间进行缓存数据,使用各种不同缓存可以单独进行操作
     *
     * @param cacheName 缓存名称
     * @param key 键
     * @param value 值
     * @param timeout 过期时间
     * @param unit 单位
     * @param <T> T
     */
    public static <T> void set(String cacheName, String key, T value, long timeout, TimeUnit unit) {
        Cache cache = getCacheManager().getCache(cacheName);
        if (cache instanceof JCacheCache jCacheCache) {
            jCacheCache.put(key, value);
        } else if (cache instanceof TransactionAwareCacheDecorator) {
            if (timeout != 0 && unit != null) {
                getRedisTemplate().opsForValue().set(cacheName + ":" + key, value, timeout, unit);
            } else {
                getRedisTemplate().opsForValue().set(cacheName + ":" + key, value);
            }
        } else {
            assert cache != null;
            cache.put(key, value);
        }
    }

    /**
     * 获取数据
     *
     * @param cacheName 缓存名称
     * @param key 键
     * @return cache
     */
    public static Cache.ValueWrapper get(String cacheName, String key) {
        Cache cache = getCacheManager().getCache(cacheName);
        if (cache == null) {
            return null;
        }
        return cache.get(key);
    }

    /**
     * 根据类型获取数据
     *
     * @param cacheName 缓存名称
     * @param key 键
     * @param type 类型
     * @param <T> class
     * @return T
     */
    public static <T> T get(String cacheName, String key, @Nullable Class<T> type) {
        Cache cache = getCacheManager().getCache(cacheName);
        if (cache == null) {
            return null;
        }
        return cache.get(key, type);
    }

    /**
     * 移除缓存数据
     *
     * @param cacheName 缓存名称
     * @param key 键
     */
    public static void delete(String cacheName, String key) {
        Objects.requireNonNull(getCacheManager().getCache(cacheName)).evict(key);
    }

    /**
     * 如果存在则移除缓存数据
     *
     * @param cacheName 缓存名称
     * @param key       键
     */
    public static void deleteIfPresent(String cacheName, String key) {
        delete(cacheName, key);
    }

    /**
     * 清除缓存名称为cacheName的所有缓存数据
     *
     * @param cacheName 缓存名称
     */
    public static void clear(String cacheName) {
        Objects.requireNonNull(getCacheManager().getCache(cacheName)).clear();
    }

    /**
     * 队列添加元素
     * @param cacheName 缓存名称
     * @param key 键
     * @param value 值
     * @param <T> T
     */
    public static <T> void leftPush(String cacheName, String key, T value) {
        Cache cache = getCacheManager().getCache(cacheName);
        if (cache instanceof JCacheCache) {
            if (queueMap.containsKey(cacheName + ":" + key)) {
                Queue<Object> queue = queueMap.get(cacheName + ":" + key);
                queue.add(value);
            } else {
                Queue<Object> queue = new LinkedList<>();
                queue.add(value);
                queueMap.put(cacheName + ":" + key, queue);
            }
        } else if (cache instanceof TransactionAwareCacheDecorator) {
            getRedisTemplate().opsForList().leftPush(cacheName + ":" + key, value);
        }
    }

    /**
     * 队列移除元素
     * @param cacheName 缓存名称
     * @param key 键
     * @return Object
     */
    public static Object rightPop(String cacheName, String key) {
        Cache cache = getCacheManager().getCache(cacheName);
        if (cache instanceof JCacheCache) {
            if (queueMap.containsKey(cacheName + ":" + key)) {
                Queue<Object> queue = queueMap.get(cacheName + ":" + key);
                if (queue.isEmpty()) {
                    return null;
                }
                return queue.remove();
            }
        } else if (cache instanceof TransactionAwareCacheDecorator) {
            return getRedisTemplate().opsForList().rightPop(cacheName + ":" + key);
        }
        return null;
    }
}
